package com.aloda.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.aloda.constant.ExceptionConstant;
import com.aloda.constant.PageConstant;
import com.aloda.constant.TeamConstant;
import com.aloda.constant.UserConstant;
import com.aloda.dto.TeamAddDTO;
import com.aloda.dto.TeamJoinDTO;
import com.aloda.dto.TeamPageByTagDTO;
import com.aloda.entity.Team;
import com.aloda.entity.TimedTeam;
import com.aloda.entity.User;
import com.aloda.entity.UserTeam;
import com.aloda.exception.BaseException;
import com.aloda.mapper.TeamMapper;
import com.aloda.mapper.TimedTeamMapper;
import com.aloda.mapper.UserMapper;
import com.aloda.mapper.UserTeamMapper;
import com.aloda.properties.SaltProperties;
import com.aloda.service.TeamService;
import com.aloda.tools.UserHolder;
import com.aloda.utils.TeamUtils;
import com.aloda.vo.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.Gson;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.DigestUtils;

import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

/**
* @author aloda
* @description 针对表【team(队伍表)】的数据库操作Service实现
* @createDate 2024-11-05 21:51:38
*/
@Service
public class TeamServiceImpl extends ServiceImpl<TeamMapper, Team> implements TeamService {

    @Resource
    private TeamMapper teamMapper;

    @Resource
    private TimedTeamMapper timedTeamMapper;

    @Resource
    private UserTeamMapper userTeamMapper;

    @Resource
    private UserMapper userMapper;

    @Resource
    private SaltProperties saltProperties;

    /**
     * 增加队伍
     *
     * @param teamAddDTO
     * @return
     */
    @Override
    @Transactional
    public TeamAddVO addTeam(TeamAddDTO teamAddDTO) {
        // 1.数据格式校验
        String teamName = teamAddDTO.getTeamName();
        if (!TeamUtils.checkTeamName(teamName)) {
            throw new BaseException(ExceptionConstant.FORMAT_ERROR);
        }
        String teamPassword = teamAddDTO.getTeamPassword();
        if (teamAddDTO.getType() == TeamConstant.CRYPT_TEAM && !TeamUtils.checkPassword(teamPassword)) {
            throw new BaseException(ExceptionConstant.FORMAT_ERROR);
        }
        Integer maxMember = teamAddDTO.getMaxMember();
        if (maxMember == null || maxMember <= 0 || maxMember > TeamConstant.MAX_MEMBER) {
            throw new BaseException(ExceptionConstant.FORMAT_ERROR);
        }
        String teamDesc = teamAddDTO.getTeamDesc();
        if (!TeamUtils.checkDescription(teamDesc)) {
            throw new BaseException(ExceptionConstant.FORMAT_ERROR);
        }
        // 检查时间
        if(teamAddDTO.getIsLimit() == TeamConstant.IS_TIMED_TEAM) {
            LocalDateTime beginTime = teamAddDTO.getBeginTime();
            LocalDateTime endTime = teamAddDTO.getEndTime();
            if (beginTime.isAfter(endTime)) {
                throw new BaseException(ExceptionConstant.FORMAT_ERROR);
            }
        }
        List<TagSimpleVO> tags = teamAddDTO.getTags();
        if(tags == null || tags.isEmpty()) {
            throw new BaseException(ExceptionConstant.TAGS_LIST_EMPTY);
        }
        // 2.1检查当前用户创建队伍数量是否到达上限
        Long currentUserId = UserHolder.getUser().getId();
        Long num = teamMapper.queryUserTeamNumById(currentUserId);
        if (num >= UserConstant.TEAM_MAX_NUM) {
            throw new BaseException(ExceptionConstant.TEAM_NUM_IS_MAX);
        }
        // 3.基本队伍数据设置
        // 3.1 生成队伍唯一队伍号
        String teamNumber = TeamUtils.getTeamNumber(currentUserId.toString());
        // 3.2 标签列表转json
        List<String> tagList = teamAddDTO.getTags().stream().map(TagSimpleVO::getText).toList();
        Gson gson = new Gson();
        String tagsToJson = gson.toJson(tagList);
        // 获取用户
        User user = userMapper.selectById(currentUserId);
        // 3.3 生成队伍
        Team team = Team.builder()
                .teamNumber(teamNumber)
                .teamName(teamAddDTO.getTeamName())
                .userId(currentUserId)
                .nickname(user.getNickname())
                .image(teamAddDTO.getImage())
                .tags(tagsToJson)
                .maxMember(teamAddDTO.getMaxMember())
                .memberNum(TeamConstant.DEFAULT_MEMBER_NUMBER)
                .teamDesc(teamAddDTO.getTeamDesc())
                .type(teamAddDTO.getType())
                .isLimit(teamAddDTO.getIsLimit())
                .isFound(teamAddDTO.getIsFound())
                .isJoin(teamAddDTO.getIsJoin())
                .teamState(TeamConstant.TEAM_DEFAULT_STATE)
                .createUser(currentUserId)
                .updateUser(currentUserId)
                .createTime(LocalDateTime.now())
                .updateTime(LocalDateTime.now())
                .build();
        // 4.其他数据设置
        // 4.1 如果队伍位加密队伍 将密码进行加密处理
        if (teamAddDTO.getType() == TeamConstant.CRYPT_TEAM) {
            teamPassword = DigestUtils.md5DigestAsHex((teamPassword + saltProperties.getTeamPasswordSalt()).getBytes());
            team.setTeamPassword(teamPassword);
        }
        // 4.2 是否附近可见
        if (teamAddDTO.getIsFound() == TeamConstant.FOUND) {
            team.setGeoX(teamAddDTO.getGeoX());
            team.setGeoY(teamAddDTO.getGeoY());
        }
        // 先插入队伍获得主键id
        teamMapper.insert(team);

        // 4.3 是否为限时队伍
        if (teamAddDTO.getIsLimit() == TeamConstant.IS_TIMED_TEAM) {
            TimedTeam timedTeam = TimedTeam.builder()
                    .teamId(team.getId())
                    .beginTime(teamAddDTO.getBeginTime())
                    .endTime(teamAddDTO.getEndTime())
                    .remainingSlots(teamAddDTO.getMaxMember())
                    .createTime(LocalDateTime.now())
                    .updateTime(LocalDateTime.now())
                    .build();
            timedTeamMapper.insert(timedTeam);
        }

        // 5.设置队伍-用户关系表
        UserTeam userTeam = UserTeam.builder()
                .userId(currentUserId)
                .teamId(team.getId())
                .isJoin(UserConstant.IS_JOIN)
                .createTime(LocalDateTime.now())
                .updateTime(LocalDateTime.now())
                .joinTime(LocalDateTime.now())
                .build();
        userTeamMapper.insert(userTeam);

        // 6.构建返回对象
        return TeamAddVO.builder()
                .teamNumber(teamNumber)
                .teamName(team.getTeamName())
                .build();
    }

    /**
     * 获取用户创建的队伍
     *
     * @param currentPage
     * @return
     */
    @Override
    public List<UserTeamPageVO> getUserCreatedTeam(Integer currentPage) {
        Page<Team> teamPage = new Page<>(currentPage,PageConstant.DEFAULT_PAGE_SIZE);
        LambdaQueryWrapper<Team> teamLambdaQueryWrapper = new LambdaQueryWrapper<>();
        Long id = UserHolder.getUser().getId();
        teamLambdaQueryWrapper.eq(Team::getUserId,id);
        Page<Team> page = teamMapper.selectPage(teamPage, teamLambdaQueryWrapper);
        List<Team> teams = page.getRecords();
        return getSafetyList(teams);
    }

    /**
     * 获取队伍详细信息
     *
     * @param id
     * @return
     */
    @Override
    public TeamInfoVO getTeamDetailInfo(Long id) {
        // 获取当前用户id
        Long userId = UserHolder.getUser().getId();
        // 创建一个vo对象
        TeamInfoVO teamInfoVO = new TeamInfoVO();
        // 判断当前用户是否为成员 select * form user_team where user_id = ? and team_id = ? and is_join = 1
        LambdaQueryWrapper<UserTeam> userTeamLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userTeamLambdaQueryWrapper.eq(UserTeam::getUserId,userId)
                .eq(UserTeam::getTeamId,id)
                .eq(UserTeam::getIsJoin,UserConstant.IS_JOIN);
        Long count = userTeamMapper.selectCount(userTeamLambdaQueryWrapper);
        if (count > 0) {
            teamInfoVO.setIsMember(UserConstant.IS_JOIN);
        }else {
            teamInfoVO.setIsMember(UserConstant.NOT_JOIN);
        }
        // 获取队伍基本信息
        Gson gson = new Gson();
        Team team = teamMapper.selectById(id);
        teamInfoVO.setId(team.getId());
        teamInfoVO.setTeamNumber(team.getTeamNumber());
        teamInfoVO.setTeamName(team.getTeamName());
        teamInfoVO.setUserId(team.getUserId());
        teamInfoVO.setNickname(team.getNickname());
        teamInfoVO.setImage(team.getImage());
        teamInfoVO.setMaxMember(team.getMaxMember());
        teamInfoVO.setMemberNum(team.getMemberNum());
        teamInfoVO.setTeamDesc(team.getTeamDesc());
        teamInfoVO.setIsLimit(team.getIsLimit());
        teamInfoVO.setType(team.getType());
        teamInfoVO.setTags(gson.fromJson(team.getTags(),List.class));
        // 判断是否为队长
        if(Objects.equals(userId, team.getUserId())){
            teamInfoVO.setIsOwner(TeamConstant.IS_LEADER);
        }else {
            teamInfoVO.setIsOwner(TeamConstant.NOT_LEADER);
        }
        // 限时队伍查询时间
        if(team.getIsLimit() == TeamConstant.IS_TIMED_TEAM){
            TimedTeam timedTeam = timedTeamMapper.selectById(id);
            teamInfoVO.setBeginTime(timedTeam.getBeginTime());
            teamInfoVO.setEndTime(timedTeam.getEndTime());
        }
        return teamInfoVO;
    }

    /**
     * 根据标签查找队伍
     *
     * @param teamPageByTagDTO
     * @return
     */
    @Override
    public List<UserTeamPageVO> getTeamByTags(TeamPageByTagDTO teamPageByTagDTO) {
        if (CollectionUtils.isEmpty(teamPageByTagDTO.getTags())) {
            throw new BaseException(ExceptionConstant.TAGS_LIST_EMPTY);
        }
        Page<Team> teamPage = new Page<>(teamPageByTagDTO.getCurrentPage(),PageConstant.DEFAULT_PAGE_SIZE);
        LambdaQueryWrapper<Team> teamLambdaQueryWrapper = new LambdaQueryWrapper<>();
        teamPageByTagDTO.getTags().forEach(tag -> {
            teamLambdaQueryWrapper.like(Team::getTags,tag);
        });
        Page<Team> page = teamMapper.selectPage(teamPage, teamLambdaQueryWrapper);
        List<Team> teamList = page.getRecords();
        return getSafetyList(teamList);
    }

    /**
     * 用户加入队伍
     *
     * @param teamJoinDTO
     * @return
     */
    @Transactional
    @Override
    public TeamJoinVO userJoinTeam(TeamJoinDTO teamJoinDTO) {
        Long userId = UserHolder.getUser().getId();
        // 判断是否已加入队伍
        LambdaQueryWrapper<UserTeam> userTeamLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userTeamLambdaQueryWrapper.eq(UserTeam::getUserId,userId).eq(UserTeam::getTeamId,teamJoinDTO.getId());
        Long count = userTeamMapper.selectCount(userTeamLambdaQueryWrapper);
        if (count > 0) {
            throw new BaseException(ExceptionConstant.USER_IS_JOIN_ERROR);
        }
        // 判断队伍人数是否剩余
        Team team = teamMapper.selectById(teamJoinDTO.getId());
        int slots = team.getMaxMember() - team.getMemberNum();
        if (slots <= 0) {
            throw new BaseException(ExceptionConstant.TEAM_MEMBER_NUM_IS_MAX);
        }
        // 判断是否为限时队伍
        if (team.getIsLimit() == TeamConstant.IS_TIMED_TEAM) {
            TimedTeam timedTeam = timedTeamMapper.selectById(team.getId());
            LocalDateTime beginTime = timedTeam.getBeginTime();
            LocalDateTime endTime = timedTeam.getEndTime();
            if(LocalDateTime.now().isBefore(beginTime)) {
                throw new BaseException(ExceptionConstant.TIMED_BEFORE_ERROR);
            }
            if (LocalDateTime.now().isAfter(endTime)) {
                throw new BaseException(ExceptionConstant.TIMED_AFTER_ERROR);
            }
        }
        // 判断是否为加密队伍
        if (team.getType() == TeamConstant.CRYPT_TEAM) {
            String password = DigestUtils.md5DigestAsHex((teamJoinDTO.getTeamPassword() + saltProperties.getTeamPasswordSalt()).getBytes());
            if (!password.equals(team.getTeamPassword())){
                throw new BaseException(ExceptionConstant.TEAM_PASSWORD_ERROR);
            }
        }
        UserTeam userTeam = UserTeam.builder()
                .teamId(team.getId())
                .userId(userId)
                .joinTime(LocalDateTime.now())
                .createTime(LocalDateTime.now())
                .updateTime(LocalDateTime.now())
                .isJoin(UserConstant.NOT_JOIN)
                .build();
        userTeamMapper.insert(userTeam);
        // 更新队伍数据
        Boolean result = teamMapper.updateMemberNum(team.getId());
        if(!result) {
            throw new BaseException(ExceptionConstant.TEAM_UPDATE_ERROR);
        }
        // 更新限时队伍数据
        if (team.getIsLimit() == TeamConstant.IS_TIMED_TEAM){
           Boolean r =  timedTeamMapper.updateRemainingSlots(team.getId());
           if(!r) {
               throw new BaseException(ExceptionConstant.TEAM_UPDATE_ERROR);
           }
        }
        return new TeamJoinVO(team.getTeamNumber(),team.getIsJoin());
    }

    /**
     * 获取用户加入的队伍（滚动分页）
     *
     * @param currentPage
     * @return
     */
    @Override
    public List<UserTeamPageVO> getUserTeam(Integer currentPage) {
        // 1.分页查询用户-队伍关系表
        Page<UserTeam> page = new Page<>(currentPage, PageConstant.DEFAULT_PAGE_SIZE);
        LambdaQueryWrapper<UserTeam> userTeamLambdaQueryWrapper = new LambdaQueryWrapper<>();
        Long id = UserHolder.getUser().getId();
        userTeamLambdaQueryWrapper.eq(UserTeam::getUserId,id);
        userTeamLambdaQueryWrapper.eq(UserTeam::getIsJoin,UserConstant.IS_JOIN);
        Page<UserTeam> userTeamPage = userTeamMapper.selectPage(page, userTeamLambdaQueryWrapper);
        if (userTeamPage.getRecords() == null || userTeamPage.getRecords().isEmpty()) {
            return Collections.emptyList();
        }
        // 2.通过查询出的用户-队伍关系，查询对应的队伍信息
        List<Long> teamIdList = userTeamPage.getRecords().stream().map(UserTeam::getTeamId).toList();
        List<Team> teams = teamMapper.selectBatchIds(teamIdList);
        // 3.将队伍信息脱敏返回
        return getSafetyList(teams);
    }

    private List<UserTeamPageVO> getSafetyList(List<Team> teams) {
        Gson gson = new Gson();
        return teams.stream().map(team -> {
            UserTeamPageVO userTeamPageVO = UserTeamPageVO.builder()
                    .id(team.getId())
                    .teamLeader(team.getNickname())
                    .teamName(team.getTeamName())
                    .image(team.getImage())
                    .currentMember(team.getMemberNum())
                    .maxMember(team.getMaxMember())
                    .teamDesc(team.getTeamDesc())
                    .build();
            List list = gson.fromJson(team.getTags(), List.class);
            userTeamPageVO.setTags(list);
            return userTeamPageVO;
        }).toList();
    }
}




